<?php

	/*
	 *            ________ ___
	 *           /   /   /\  /\       Konsolidate
	 *      ____/   /___/  \/  \
	 *     /           /\      /      http://konsolidate.klof.net
	 *    /___     ___/  \    /
	 *    \  /   /\   \  /    \       Class:  CoreDevNetworkProtocolHTTP
	 *     \/___/  \___\/      \      Tier:   CoreDev
	 *      \   \  /\   \  /\  /      Module: Network/Protocol/HTTP
	 *       \___\/  \___\/  \/
	 *         \          \  /        $Rev: 1 $
	 *          \___    ___\/         $Author: john $
	 *              \   \  /          $Date: 2008-06-03 15:02:39 +0200 (Tue, 03 Jun 2008) $
	 *               \___\/
	 */


	/**
	 *  Suggestion for addition to CoreNetworkProtocolHTTP
	 *  @name    CoreDevNetworkProtocolHTTP
	 *  @type    class
	 *  @package Konsolidate
	 *  @author  John Beitler <john.beitler@advance.nl>
	 */
	class SiteNetworkProtocolHTTP extends CoreNetworkProtocolHTTP
	{
		/**
		 *  Build up the entire request
		 *  @access private
		 *  @param  string $sMethod   The request method to use
		 *  @param  mixed  $mReferer  The referer to provide
		 *  @return string
		 */
		function buildRequestString( $sMethod="GET", $mReferer=false, $sData="" )
		{
			$sBoundary = str_pad( substr( md5( time() ), 0, 12 ), 40, "-", STR_PAD_LEFT );
			$sMethod   = strToUpper( $sMethod );
			$sData     = empty( $sData ) ? $this->buildDataString( $sMethod, $sBoundary ) : $sData;

			// Set or override headers
			$this->setHeader(
				Array(
					"Host"=>$this->host,
					"User-Agent"=>( !empty( $this->useragent ) ? "{$this->useragent} " : "" ) . "HTTPRequest/{$this->version} (PHP Class; klof++ 2005)",
					"Referer"=>( !empty( $mReferer ) ? $mReferer : "http://{$_SERVER[ "HTTP_HOST" ]}{$_SERVER["REQUEST_URI"]}" ),
					"Connection"=>"close"
				)
			);
			$sRequest  = "{$sMethod} {$this->path}" . ( $sMethod == "GET" && !empty( $sData ) ? "?$sData" : "" ) . " HTTP/1.1\n";

			if ( count( $this->headerdata ) > 0 )
				foreach( $this->headerdata as $sKey=>$sValue )
					if ( !empty( $sValue ) )
						$sRequest .= "{$sKey}: {$sValue}\n";

			if ( $sMethod == "POST" )
			{
				$sRequest .= "Content-type: " . ( $this->multiform ? "multipart/form-data; boundary={$sBoundary}" : "application/x-www-form-urlencoded" ) . "\n";
				$sRequest .= "Content-length: " . strlen( $sData ) . "\r\n\r\n";
				$sRequest .= "{$sData}";
			}
			$sRequest .= "\r\n\r\n";

			return $sRequest;
		}

		/**
		 *  prepare and perform an actual request
		 *  @access private
		 *  @param  string $sMethod   The request method to use
		 *  @param  string $sURL      The URL to request
		 *  @param  array  $aData     additional paramaters to send (use key=>value pairs)
		 *  @param  mixed  $mReferer  The referer to provide
		 *  @return string
		 */
		function request( $sMethod, $sURL, $sData="", $mReferer=false )
		{
			//  Files cannot be transmitted with a GET request, so even if files were added, we do not use multipart/form-data
			if ( strToUpper( $sMethod ) == "GET" && $this->multiform )
				$this->multiform = false;

			//  Prepare all request URL requirements
			$this->parseURL( $sURL );

			//  Prepare the actual request
			$sRequest = $this->buildRequestString( $sMethod, $mReferer, $sData );

			//  Open the connection, post the data and read the feedback
			$fpConn = @fsockopen( $this->host, $this->port );
			if ( $fpConn )
			{
				$aResult = Array(
					"header"=>Array(),
					"content"=>""
				);
				$bHeader         = true;
				$bChunkedTranfer = false;
				$bBeginChunk     = false;
				$nReadBytes      = 1024;

				fputs( $fpConn, $sRequest, strLen( $sRequest ) );
				while( !feof( $fpConn ) )
				{
					$sResult = fgets( $fpConn, $nReadBytes );
					$sTrim   = trim( $sResult );

					if ( empty( $sTrim ) && $bHeader ) // determine wether or not the header has ended (this empty line is not added to either the header or the content)
					{
						$bHeader = false;
						$this->parseHeader( $aResult[ "header" ] );

						if ( $this->getHeader( "status" ) != 200 )
						{
							return false;
						}
						else if ( $this->getHeader( "Transfer-Encoding" ) == "chunked" ) // if the content is delivered in chunks, we need to handle the content slightly different
						{
							$bChunkedTranfer = true;
							$bBeginChunk     = true;
						}
					}
					elseif ( $bHeader ) // add the result to the header array
					{
						$aResult[ "header" ][] = $sTrim;
					}
					else // add the result to the content string
					{
						if ( $bChunkedTranfer ) // we should handle chunked data delivery
						{
							if ( $bBeginChunk ) // we are at the beginning of an era (chunk wise)
							{
								$bBeginChunk = false;
								$nReadBytes  = hexdec( $sTrim ); // chunk sizes are provided as HEX values
								unset( $sResult ); // clear sResult
							}
							else if ( is_numeric( $sTrim ) && $sTrim == 0 ) // the end of the chunk has been reached
							{
								$nBeginChunk = true;
								$nReadBytes  = 1024;
								unset( $sResult ); // clear sResult
							}
						}
						if ( !empty( $sResult ) ) // do we have content?
							$aResult[ "content" ] .= $sResult;
					}
				}
				fclose( $fpConn );

				$this->triggerStatusHandler( $this->getResponseStatus() );

				return $aResult[ "content" ];
			}
			return false;
		}
	}

?>